#include <stdio.h>
#include <string>
#include <dirent.h>
#include <sys/types.h>
#include "BitImage.h"
#include "JpegImage.h"
#include "PngImage.h"
#include "Image.h"
#include "Conver.h"
#include "Param.h"
#include "image_scale.h"

using namespace std;

static bool DecodeImageConverFile(ConverParam *param, Image *img);
static uint8_t *rgb888_to_argb888(const uint8_t *src, int pixel);

static const char *imageErrorString[] =
    {
        "image not init",
        "can not found image",
        "not a legal image, only support for .bmp .jpg/jpeg .png",
};

int main(int argc, const char *argv[])
{
    if (argc == 1)
    {
        printf("Plese add \"-h\" param to show help information\n");
        return 0;
    }

    int error;
    ConverParam *param = ConverParamDecode(argc, argv, &error); // 进行参数解析
    if (error != 0)
    {
        printf("param error: %s\n", ParamErrorToString(error));
        return 0;
    }
    if (param == nullptr) // error为0但param为nullptr表示显示帮助信息(已显示)
        return 0;

    Image *img = nullptr;
    DIR *inputDir = nullptr;
    struct dirent *fileInfo = nullptr;

    if (param->inputPath != "") // 批量转换目录打开
    {
        inputDir = opendir(param->inputPath.c_str()); // 打开目录
        if (inputDir == nullptr)                      // 目录不存在，退出
        {
            printf("can not open directory: %s\n", param->inputPath.c_str());
            return 0;
        }
    }

    do
    {
        if (inputDir != nullptr)
        {
            fileInfo = readdir(inputDir);
            if (fileInfo == nullptr) // 目录已读取完毕
                break;

            param->inputFileName = param->inputPath + string(fileInfo->d_name);

            param->outFileName = FileGetNameWithoutExt(string(fileInfo->d_name)); // 减去文件扩展名
            param->outFileName += param->outFile ? ".bin" : ".c";                 // 构造输出文件名
        }

        const char *pfile = strrchr(param->inputFileName.c_str(), '.'); // 获取文件后缀
        if (!strcasecmp(pfile, ".bmp"))
            img = new BitImage(param->inputFileName);
        else if (!strcasecmp(pfile, ".jpg") || !strcasecmp(pfile, ".jpeg"))
            img = new JpegImage(param->inputFileName);
        else if (!strcasecmp(pfile, ".png"))
            img = new PngImage(param->inputFileName);
        else
            continue;

        if (img != nullptr)
        {
            printf("ready to conver \"%s\" ......\n", param->inputFileName.c_str());

            DecodeImageConverFile(param, img); // 转换图像到目标文件

            delete img;
            img = nullptr;
        }

    } while (inputDir != nullptr); // 单次转换，inputDir必为nullptr，只循环1次

    if (inputDir != nullptr)
        closedir(inputDir); // 关闭目录

    return 0;
}

/**
 * @brief 解码图片并转换为对应文件
 */
static bool DecodeImageConverFile(ConverParam *param, Image *img)
{
    Image::ImageStatus status = img->GetStatus();
    if (status != Image::IMAGE_OK) // 图片解码失败
    {
        printf("conver %s failed! error:%s\n", param->inputFileName.c_str(), imageErrorString[abs(status) - 1]);
        return 0;
    }

    // 获取图片信息
    Image::ImageInfo info = img->GetInfo();

    int h = info.height;
    int w = info.width;
    int bpp = info.bpp;

    uint8_t *imgBuf = new uint8_t[w * h * bpp];
    uint8_t *scaleImgBuf = imgBuf;
    img->GetImage(imgBuf, h);
    if (info.srcBpp == 24)
    {
        imgBuf = rgb888_to_argb888(imgBuf, w * h); // 添加alpha通道
        delete[] scaleImgBuf;

        scaleImgBuf = imgBuf;
    }

    if (param->width > 0 || param->height > 0)
    {
        if (param->width > 0)
            w = param->width;

        if (param->height > 0)
            h = param->height;

        scaleImgBuf = (uint8_t *)image_bicubic_scale_argb32((void *)imgBuf, info.width, info.height, (float)w / info.width, (float)h / info.height);
        delete[] imgBuf;

        info.width = w;
        info.height = h;
    }

    // 创建转换目标文件
    string outFile = param->outPath + param->outFileName; // 目标文件名
    FILE *file = fopen(outFile.c_str(), "w");
    if (file == NULL)
        return false;

    // 若输出文件名有后缀，则删除，C变量名不能出现'.'
    int dotPos = param->outFileName.find_last_of('.', string::npos);
    if (dotPos != string::npos) // 找到后缀
        param->outFileName = param->outFileName.substr(0, dotPos);

    info.name = param->outFileName;                       // 设置C输出名
    uint32_t cfg = param->format | (param->imgcfg << 30); // 设置转换参数

    // 转换图片，并写入文件
    ConverToFile(file, scaleImgBuf, info, cfg, (ConverFile)param->outFile);

    fclose(file);

    printf("conver \"%s\" complete!\n", param->inputFileName.c_str());

    delete[] scaleImgBuf;

    return true;
}

/**
 * @brief 在rgb888图片数据中添加alpha通道，alpha数值0xff
 * @param src rgb888图片数据
 * @param pixel 像素点个数 
*/
static uint8_t *rgb888_to_argb888(const uint8_t *src, int pixel)
{
    uint8_t *argb888 = new uint8_t[pixel * 4];

    for (int i = 0; i < pixel; i++)
    {
        argb888[4 * i] = src[3 * i];
        argb888[4 * i + 1] = src[3 * i + 1];
        argb888[4 * i + 2] = src[3 * i + 2];
        argb888[4 * i + 3] = 0xff;
    }

    return argb888;
}
